<?php


/**
 *
 *
 * Method _fileGetContents and _filePutContents from Zend_Cache.
 */
class base_templateEngine_Cache {

    protected $cacheDir  = '';
    protected $cacheTime = 0;
    protected $fileLocking = true;


    public static $DATA_DIR  = 'data';
    public static $IMAGE_DIR = 'images';
    public static $IMAGEMETA_DIR = 'imagemetadata';
    public static $PAGE_DIR  = 'pages';

    public static $CLEAR_ALL = 'a';
    public static $CLEAR_OLD = 'o';

    /**
     *
     * @param string $subdir
     * @param bool $locking Lock file while saving and reading?
     * @param int $cachetime -1 uses cfgCacheTime config
     */
    public function  __construct($subdir = 'pages', $locking = true, $cachetime = -1) {
        $this->fileLocking = $locking;
        $this->cacheDir  = base_ApplicationContext::getInstance()->cfgPath('cacheDir').$subdir.DIRECTORY_SEPARATOR;
        $this->cacheTime = $cachetime==-1?base_ApplicationContext::getInstance()->cfg('CacheTime'):$cachetime;
    }

    /**
     *
     * @return string The set cache dir
     */
    public function getCacheDir() {
        return $this->cacheDir;
    }

    public function save($key, $value) {
        $filename = $this->fileName($this->prepareName($key));
        if (!strlen($filename)) throw new Exception ("Cache: Filename to short '$key:$filename'");
        if (!is_array($value) && !strlen($value)) throw new Exception ("Cache: No data to save '$key:$filename'");
        $parts = preg_split('#/#', dirname($filename));
        $count = count($parts);
        $i = 0;
        $file = "";
        while ($count) {
            $file .= ($i>0?DIRECTORY_SEPARATOR:'').$parts[$i];
            if (!file_exists($this->cacheDir.$file))
                mkdir($this->cacheDir.$file);
            $i++;
            $count--;
        }
        $this->_save($filename, $value);
    }

    public function delete($key) {
        $filename = $this->fileName($this->prepareName($key));
        if (is_file($this->cacheDir.$filename)) {
            @unlink($this->cacheDir.$filename);
        }
    }

    public function clean ($mode = 'o') {
        if ($this->cacheTime == 0 && $mode == self::$CLEAR_OLD) {
            return;
        }
        $d = dir($this->cacheDir);
        while (false !== ($entry = $d->read())) {
            if ($entry[0] == '.') continue;
            $d2 = dir($this->cacheDir . $entry);
            while (false !== ($entry2 = $d2->read())) {
                if ($entry2[0] == '.') continue;
                $filename = $this->cacheDir . $entry . DIRECTORY_SEPARATOR . $entry2;
                if ($mode == self::$CLEAR_ALL) {
                    if (is_file($filename)) {
                        @unlink($filename);
                    }
                    continue;
                }
                if ($this->cacheTime == 0) {
                    return;
                } else {
                    $filemtime = filemtime($filename);
                    $time = time();
                    if ($filemtime && ($time - $filemtime >= $this->cacheTime)) {
                        @unlink($filename);
                    }
                }
            }
            $d2->close();
        }
        $d->close();
    }

    private function _save($filename, $value, $tags = array()) {
        if (is_array($value)) {
            $this->_filePutContents($this->cacheDir.$filename.'-ser', serialize($value));
        } else {
            $this->_filePutContents($this->cacheDir.$filename, $value);
        }
    }

    public function load($key) {
        $filename = $this->fileName($this->prepareName($key));
        if (is_file($this->cacheDir.$filename.'-ser')) {
            return unserialize($this->_fileGetContents($this->cacheDir.$filename.'-ser'));
        } elseif (is_file($this->cacheDir.$filename)) {
            return $this->_fileGetContents($this->cacheDir.$filename);
        }
    }

    public function getFileName($key) {
        if ($type = $this->test($key)) {
            $filename = $this->fileName($this->prepareName($key));
            return $this->cacheDir.$filename;
        }
    }



    /**
     * No / \ and : allowed ids => will be replaced by _
     * @param string $key
     */
    public function test($key) {
        clearstatcache();
        $key = $this->prepareName($key);
        $check = $this->checkFile($this->fileName($key));
        return $check;
    }

    private function prepareName($key) {
        return preg_replace('#/#', '_', $key);
    }


    /**
     * Override this method to generate different filenames
     * @param string $key
     * @return string
     */
    protected function fileName($key) {
        //$key = bin2hex(mhash(MHASH_SHA256,$key));
        //$key = md5($key);
        //$key = sha1($key);
        if (strlen($key) > 2) {
            /*if (strlen($key) > 4) {
                /*if (strlen($key) > 6) {
                    $key = substr($key, 0, 6).'/'.substr($key, 6);
                }
                $key = substr($key, 0, 4).'/'.substr($key, 4);
            }*/
            $key = substr($key, 0, 2).'/'.substr($key, 2);
        }
        return $key;
    }

    /**
     * Return the file content of the given file
     *
     * @param  string $file File complete path
     * @return string File content (or false if problem)
     */
    protected function _fileGetContents($file)
    {
        $result = false;
        if (!is_file($file)) {
            return false;
        }
        $f = @fopen($file, 'rb');
        if ($f) {
            if ($this->fileLocking) @flock($f, LOCK_SH);
            $result = stream_get_contents($f);
            if ($this->fileLocking) @flock($f, LOCK_UN);
            @fclose($f);
        }
        return $result;
    }

    /**
     * Put the given string into the given file
     *
     * @param  string $file   File complete path
     * @param  string $string String to put in file
     * @return boolean true if no problem
     */
    protected function _filePutContents($file, $string)
    {
        $result = false;
        $f = @fopen($file, 'ab+');
        if ($f) {
            if ($this->fileLocking) @flock($f, LOCK_EX);
            fseek($f, 0);
            ftruncate($f, 0);
            $tmp = @fwrite($f, $string);
            if (!($tmp === false)) {
                $result = true;
            }
            @fclose($f);
        }
        //@chmod($file, $this->cacheFileUmask);
        return $result;
    }

    private function checkFile($filename) {
        $type = '';
        if (is_file($this->getCacheDir().$filename)) {
            $fname = $this->getCacheDir().$filename;
            $type  = 'string';
        } elseif (is_file($this->getCacheDir().$filename.'-ser')) {
            $fname = $this->getCacheDir().$filename.'-ser';
            $type  = 'array';
        } else {
            return;
        }
        if ($this->cacheTime == 0) {
            return $type;
        } else {
            $filemtime = filemtime($fname);
            $time = time();
            if ($filemtime && ($time - $filemtime < $this->cacheTime)) {
                return $type;
            }
        }
    }

}